package cn.minis.beans.factory.support;

import cn.minis.beans.factory.config.ConstructorArgumentValue;
import cn.minis.beans.factory.config.ConstructorArgumentValues;
import cn.minis.beans.factory.config.BeanDefinition;
import cn.minis.beans.BeanFactory;
import cn.minis.beans.BeansException;
import cn.minis.beans.factory.config.PropertyValue;
import cn.minis.beans.factory.config.PropertyValues;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SimpleBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
  //private List<BeanDefinition> beanDefinitions = new ArrayList<>();
  //private List<String> beanNames = new ArrayList<>();
  //private Map<String, Object> singletons = new HashMap<>();
  
  private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
  private List<String> beanDefinitionNames = new ArrayList<>();
  
  
  @Override
  public boolean isSingleton(String name) {
    return this.beanDefinitionMap.get(name).isSingleton();
  }
  
  @Override
  public boolean isPrototype(String name) {
    return this.beanDefinitionMap.get(name).isPrototype();
  }
  
  
  @Override
  public Class<?> getType(String name) {
    return this.beanDefinitionMap.get(name).getClass();
  }
  
  
  //private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>(256);
  
  public SimpleBeanFactory() {
  }
  
  //getBean，容器的核心方法
  @Override
  public Object getBean(String beanName) throws BeansException {
    // 先尝试直接拿Bean实例
    Object singleton = singletons.get(beanName);
    //如果此时还没有这个Bean的实例，则获取它的定义来创建实例
  
    if (singleton != null) {
      return singleton;
    }
  
    // 提前暴露的,未赋值属性的对象
    singleton = this.earlySingletonObjects.get(beanName);
    if (singleton != null) { 
      return singleton;
    }
  
    int i = beanDefinitionNames.indexOf(beanName);
    if (i == -1) {
      throw new BeansException(beanName + " Bean定义不存在");
    }
    
    //获取Bean的定义
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    try {
      singleton = createBean(beanDefinition);
    } catch (Exception e) {
      e.printStackTrace();
    }
  
    //新注册这个bean实例 
    this.registerSingleton(beanName, singleton);
    //注册Bean实例
    //singletons.put(beanDefinition.getId(), singleton);
    
    return singleton;
  }
  
/*  @Override
  public void registerBeanDefinition(BeanDefinition beanDefinition) {
    this.beanDefinitions.put(beanDefinition.getId(), beanDefinition);
    //this.beanNames.add(beanDefinition.getId());
  }*/
  
  @Override
  public Boolean containsBean(String name) {
    return containsSingleton(name);
  }
  
  /*@Override
  public void registerBean(String beanName, Object obj) {
    this.registerSingleton(beanName, obj);
  }*/
  
  public Object createBean(BeanDefinition beanDefinition) {
    Class<?> clz = null;
    Object obj = null;
    Constructor<?> con = null;
    
    try {
      clz = Class.forName(beanDefinition.getClassName());
      // 处理构造器参数
      ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
      // 如果有参数
      if (!constructorArgumentValues.isEmpty()) {
        Class<?>[] paramTypes = new Class<?>[constructorArgumentValues.getArgumentCount()];
        Object[] paramValues = new Object[constructorArgumentValues.getArgumentCount()];
        //对每一个参数，分数据类型分别处理
        for (int i = 0; i < constructorArgumentValues.getArgumentCount(); i++) {
          ConstructorArgumentValue constructorArgumentValue =
            constructorArgumentValues.getIndexedArgumentValue(i);
          if ("String".equals(constructorArgumentValue.getType()) || "java.lang.String".equals(constructorArgumentValue.getType())) {
            paramTypes[i] = String.class;
            paramValues[i] = constructorArgumentValue.getValue();
          } else if ("Integer".equals(constructorArgumentValue.getType()) || "java.lang.Integer".equals(constructorArgumentValue.getType())) {
            paramTypes[i] = Integer.class;
            paramValues[i] = Integer.valueOf((String) constructorArgumentValue.getValue());
          } else if ("int".equals(constructorArgumentValue.getType())) {
            paramTypes[i] = int.class;
            paramValues[i] = Integer.valueOf((String) constructorArgumentValue.getValue());
          } else { //默认为string
            paramTypes[i] = String.class;
            paramValues[i] = constructorArgumentValue.getValue();
          }
        }
        try {
          //按照特定构造器创建实例
          con = clz.getConstructor(paramTypes);
          obj = con.newInstance(paramValues);
        } catch (Exception e) {
          e.printStackTrace();
        }
      } else { //如果没有参数，直接创建实例
        obj = clz.newInstance();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  
    if (obj != null) {
      // 提前暴露
      earlySingletonObjects.put(beanDefinition.getId(), obj);
    }
  
    handleProperties(beanDefinition, clz, obj);
    return obj;
  }
  
  private void handleProperties(BeanDefinition beanDefinition, Class<?> clz, Object obj) {
    // 处理属性
    PropertyValues propertyValues = beanDefinition.getPropertyValues();
    if (!propertyValues.isEmpty()) {
      for (int i = 0; i < propertyValues.size(); i++) {
        //对每一个属性，分数据类型分别处理
        PropertyValue propertyValue = propertyValues.getPropertyValueList().get(i);
        String pType = propertyValue.getType();
        String pName = propertyValue.getName();
        Object pValue = propertyValue.getValue();
        boolean ref = propertyValue.getIsRef();
        
        // 没有依赖其他对象,直接初始化
        Class<?>[] paramTypes = new Class<?>[1];
        Object[] paramValues = new Object[1];
        if (!ref) {
          if ("String".equals(pType) || "java.lang.String".equals(pType)) {
            paramTypes[0] = String.class;
          } else if ("Integer".equals(pType) || "java.lang.Integer".equals(pType)) {
            paramTypes[0] = Integer.class;
          } else if ("int".equals(pType)) {
            paramTypes[0] = int.class;
          } else { // 默认为string
            paramTypes[0] = String.class;
          }
          paramValues[0] = pValue;
        }
        // 依赖其他对象,先创建其他对象
        else {
          try {
            paramTypes[0] = Class.forName(pType);
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
          try {
            //再次调用getBean创建ref的bean实例
            paramValues[0] = getBean((String) pValue);
          } catch (BeansException e) {
            e.printStackTrace();
          }
        }
        
        //按照setXxxx规范查找setter方法，调用setter方法设置属性
        String methodName = "set" + pName.substring(0, 1).toUpperCase() + pName.substring(1);
        Method method = null;
        try {
          method = clz.getMethod(methodName, paramTypes);
        } catch (Exception e) {
          e.printStackTrace();
        }
        try {
          method.invoke(obj, paramValues);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      
    }
  }
  
  public void refresh() {
    for (String beanName : beanDefinitionNames) {
      try {
        getBean(beanName);
      }catch (BeansException e) {
        e.printStackTrace();
      }
    }
  }  
  
  public Object doCreateBean(BeanDefinition beanDefinition) {
    Class<?> clazz = null;
    Object obj = null;
    Constructor<?> constructor = null;
  
  
    try {
       clazz = Class.forName(beanDefinition.getClassName());
      // handle constructor
      ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
      
      if (constructorArgumentValues.isEmpty()) {
        return obj;
      }
      
      // 构造函数
      Class<?>[] paramTypes = new Class<?>[constructorArgumentValues.getArgumentCount()];
      Object[] paramValues = new Object[constructorArgumentValues.getArgumentCount()];
      for (int i = 0; i < constructorArgumentValues.getArgumentCount(); i++) {
        ConstructorArgumentValue constructorArgumentValue = constructorArgumentValues.getIndexedArgumentValue(i);
        if ("String".equals(constructorArgumentValue.getType()) || "java.lang.String".equals(constructorArgumentValue.getType())) {
          paramTypes[i] = String.class;
          paramValues[i] = constructorArgumentValue.getValue();
        } else if ("Integer".equals(constructorArgumentValue.getType()) || "java.lang.Integer".equals(constructorArgumentValue.getType())) {
          paramTypes[i] = Integer.class;
          paramValues[i] = Integer.valueOf((String) constructorArgumentValue.getValue());
        } else if ("int".equals(constructorArgumentValue.getType())) {
          paramTypes[i] = int.class;
          paramValues[i] = Integer.valueOf((String) constructorArgumentValue.getValue());
        } else {
          paramTypes[i] = String.class;
          paramValues[i] = constructorArgumentValue.getValue();
        }
      }
  
  
      try {
        // 参数类型顺序不匹配 则会异常
        constructor = clazz.getConstructor(paramTypes);
        obj = constructor.newInstance(paramValues);
      } catch (Exception e) {
        e.printStackTrace();
      }
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
    return obj;
  }
}